import {
    clusterizeFlatTree,
    flatTree,
    getFlatTreeMinMax,
    metaClusterizeFlatTree,
    reclusterizeClusteredFlatTree,
} from './utils/tree-clusters';
import Color from 'color';
import UIPlugin from './ui-plugin';
import {
    ClusterizedFlatTree,
    ClusterizedFlatTreeNode,
    Colors,
    FlameChartNodes,
    FlatTree,
    FlatTreeNode,
    HitRegion,
    MetaClusterizedFlatTree,
    RegionTypes,
} from '../types';
import { OffscreenRenderEngine } from '../engines/offscreen-render-engine';
import { SeparatedInteractionsEngine } from '../engines/separated-interactions-engine';
const selectedNodes=new Map();

type ClusterNode = { data: FlatTreeNode; type: string };

const DEFAULT_COLOR = Color.hsl(180, 30, 70);
type NodeClickOptions = {
    color?: string;
    type?: 'stroke' | 'background';
};

export class FlameChartPlugin extends UIPlugin {
    private nodeClickOptions: NodeClickOptions = {
        color: 'green',
        type: 'stroke'
    };
    height = 'flexible' as const;
    
    data: FlameChartNodes;
    userColors: Colors;
    flatTree: FlatTree = [];
    positionY = 0;
    colors: Colors = {};
    selectedRegion: ClusterNode | null = null;
    hoveredRegion: ClusterNode | null = null;
    lastRandomColor: typeof DEFAULT_COLOR = DEFAULT_COLOR;
    metaClusterizedFlatTree: MetaClusterizedFlatTree = [];
    actualClusterizedFlatTree: ClusterizedFlatTree = [];
    initialClusterizedFlatTree: ClusterizedFlatTree = [];
    lastUsedColor: string | null = null;
    renderChartTimeout = -1;
    customRenderFlameGrapToolTip?: (allTexts: { text: string }[]) => void;
    selectedColor?:string;
    isMultiple:boolean;

    constructor({
        data,
        colors = {},
        customRenderFlameGrapToolTip,
        name = 'flameChartPlugin',
        isMultiple=false,
        selectedColor="#3bd1ec"
    }: {
        data: FlameChartNodes;
        colors?: Colors;
        name?: string;
        customRenderFlameGrapToolTip?: (allTexts: { text: string }[]) => void;
        selectedColor?:string;
        isMultiple:boolean
    }) {
        super(name);
        this.customRenderFlameGrapToolTip = customRenderFlameGrapToolTip
        this.data = data;
        this.userColors = colors;

        this.parseData();
        this.reset();
        this.selectedColor=selectedColor;
        this.isMultiple=isMultiple;
    }
    setNodeClickOptions(options: NodeClickOptions) {
        this.nodeClickOptions = { ...this.nodeClickOptions, ...options };
    }

    override init(renderEngine: OffscreenRenderEngine, interactionsEngine: SeparatedInteractionsEngine) {
        super.init(renderEngine, interactionsEngine);
        this.interactionsEngine.on('change-position', this.handlePositionChange.bind(this));
        this.interactionsEngine.on('select', this.handleSelect.bind(this));
        this.interactionsEngine.on('hover', this.handleHover.bind(this));
        this.interactionsEngine.on('up', this.handleMouseUp.bind(this));
        this.initData();
    }
    handlePositionChange({ deltaX, deltaY }: { deltaX: number; deltaY: number }) {
        const startPositionY = this.positionY;
        const startPositionX = this.renderEngine.parent.positionX;

        this.interactionsEngine.setCursor('grabbing');

        if (this.positionY + deltaY >= 0) {
            this.setPositionY(this.positionY + deltaY);
        } else {
            this.setPositionY(0);
        }

        this.renderEngine.tryToChangePosition(deltaX);

        if (startPositionX !== this.renderEngine.parent.positionX || startPositionY !== this.positionY) {
            this.renderEngine.parent.render();
        }
    }

    handleMouseUp() {
        this.interactionsEngine.clearCursor();
    }

    setPositionY(y: number) {
        this.positionY = y;
    }

    reset() {
        this.colors = {};
        this.lastRandomColor = DEFAULT_COLOR;

        this.positionY = 0;
        this.selectedRegion = null;
    }

    calcMinMax() {
        const { flatTree } = this;

        const { min, max } = getFlatTreeMinMax(flatTree);

        this.min = min;
        this.max = max;
    }

    handleSelect(region: HitRegion<ClusterizedFlatTreeNode>) {
        const selectedRegion = this.findNodeInCluster(region);
        // 多选
        if(this.isMultiple&&selectedRegion?.data?.source?.id){
            const currentNodeKey=selectedRegion?.data?.source?.id?(selectedRegion?.data?.source?.id):(this.selectedRegion?.data?.source?.id);
            const level=selectedRegion?.data?.source?.id?(selectedRegion?.data?.level):(this.selectedRegion?.data?.level);
            const currentNode = selectedRegion?.data?.source?.id?(selectedRegion?.data?.source):(this.selectedRegion?.data?.source);
            if(selectedNodes.has(currentNodeKey)){
                selectedNodes.delete(currentNodeKey)
            }else{
                selectedNodes.set(currentNodeKey,{level,...currentNode})
            }
        }
        if (this.selectedRegion !== selectedRegion) {
            this.selectedRegion = selectedRegion;
            this.renderEngine.render();

            this.emit('select', { node: this.selectedRegion?.data ?? null, type: 'flame-chart-node' });
        }
    }

    handleHover(region: HitRegion<ClusterizedFlatTreeNode>) {
        this.hoveredRegion = this.findNodeInCluster(region);
    }

    findNodeInCluster(region: HitRegion<ClusterizedFlatTreeNode>): ClusterNode | null {
        const mouse = this.interactionsEngine.getMouse();

        if (region && region.type === RegionTypes.CLUSTER) {
            const hoveredNode = region.data.nodes.find(({ level, source: { start, duration } }) => {
                const { x, y, w } = this.calcRect(start, duration, level);

                return mouse.x >= x && mouse.x <= x + w && mouse.y >= y && mouse.y <= y + this.renderEngine.blockHeight;
            });

            if (hoveredNode) {
                return {
                    data: hoveredNode,
                    type: 'node',
                };
            }
        }

        return null;
    }

    getColor(type: string = '_default', defaultColor?: string) {
        if (defaultColor) {
            return defaultColor;
        } else if (this.colors[type]) {
            return this.colors[type];
        } else if (this.userColors[type]) {
            const color = new Color(this.userColors[type]);

            this.colors[type] = color.rgb().toString();

            return this.colors[type];
        }

        this.lastRandomColor = this.lastRandomColor.rotate(27);
        this.colors[type] = this.lastRandomColor.rgb().toString();

        return this.colors[type];
    }

    setData(data: FlameChartNodes) {
        this.data = data;

        this.parseData();
        this.initData();

        this.reset();

        this.renderEngine.recalcMinMax();
        this.renderEngine.resetParentView();
    }

    parseData() {
        this.flatTree = flatTree(this.data);

        this.calcMinMax();
    }

    initData() {
        this.metaClusterizedFlatTree = metaClusterizeFlatTree(this.flatTree);
        this.initialClusterizedFlatTree = clusterizeFlatTree(
            this.metaClusterizedFlatTree,
            this.renderEngine.zoom,
            this.min,
            this.max,
        );

        this.reclusterizeClusteredFlatTree();
    }

    reclusterizeClusteredFlatTree() {
        this.actualClusterizedFlatTree = reclusterizeClusteredFlatTree(
            this.initialClusterizedFlatTree,
            this.renderEngine.zoom,
            this.renderEngine.positionX,
            this.renderEngine.positionX + this.renderEngine.getRealView(),
        );
    }

    calcRect(start: number, duration: number, level: number) {
        const w = duration * this.renderEngine.zoom;
        return {
            x: this.renderEngine.timeToPosition(start),
            y: level * (this.renderEngine.blockHeight + 1) - this.positionY,
            w: w <= 0.1 ? 0.1 : w >= 3 ? w - 1 : w - w / 3,
        };
    }

    override renderTooltip() {
        if (this.hoveredRegion) {
            if (this.renderEngine.options.tooltip === false) {
                return true;
            } else if (typeof this.renderEngine.options.tooltip === 'function') {
                this.renderEngine.options.tooltip(
                    this.hoveredRegion,
                    this.renderEngine,
                    this.interactionsEngine.getGlobalMouse(),
                );
            } else {
                const {
                    data: {
                        source: { start, duration, name, children },
                    },
                } = this.hoveredRegion;
                const timeUnits = this.renderEngine.getTimeUnits();

                const selfTime = duration - (children ? children.reduce((acc, { duration }) => acc + duration, 0) : 0);
                if (this.customRenderFlameGrapToolTip && typeof this.customRenderFlameGrapToolTip === 'function') {
                    const currentNodeInfo = this.hoveredRegion.data.source;
                    const formatData = this.customRenderFlameGrapToolTip(currentNodeInfo).map(item => ({
                        text: `${item.key}：${item.value}`
                    }));

                    // 计算最大文本宽度
                    this.renderEngine.setCtxFont(this.renderEngine.styles.font);
                    const maxWidth = Math.max(
                        ...formatData.map(item => this.renderEngine.ctx.measureText(item.text).width)
                    );

                    // 获取鼠标位置
                    const mouseX = this.interactionsEngine.getMouse().x;
                    const globalMouse = this.interactionsEngine.getGlobalMouse();
                    const adjustedPosition = {
                        x: globalMouse.x,
                        y: globalMouse.y
                    };

                    // 如果鼠标位置超过画布宽度的一半，将 tooltip 显示在左侧
                    if (mouseX > this.renderEngine.width / 2) {
                        adjustedPosition.x -= (maxWidth + 20); // 添加20px的内边距
                    }

                    this.renderEngine.renderTooltipFromData(
                        formatData,
                        adjustedPosition,
                    );
                } else {
                    const nodeAccuracy = this.renderEngine.getAccuracy() + 2;
                    const header = `${name}`;
                    const dur = `duration: ${duration.toFixed(nodeAccuracy)} ${timeUnits} ${children?.length ? `(self ${selfTime.toFixed(nodeAccuracy)} ${timeUnits})` : ''
                        }`;
                    const st = `start: ${start.toFixed(nodeAccuracy)}`;
                    this.renderEngine.renderTooltipFromData(
                        [{ text: header }, { text: dur }, { text: st }],
                        this.interactionsEngine.getGlobalMouse(),
                    );
                }



            }

            return true;
        }

        return false;
    }
    // 循环添加背景颜色
    addBackgroundColorToNodes(selectNodes: Map<any,FlatTreeNode>, color: string='red') {
        const allNodes= Array.from(selectNodes.values());
        allNodes.forEach(node=>{
            const {start,duration,level}=node;
            const {x,y,w}=this.calcRect(start,duration,level);
            this.renderEngine.addRect({ color: color, pattern: undefined, x, y, w }, 0);
        })
    }

    override render() {
        const { width, blockHeight, height, minTextWidth } = this.renderEngine;
        console.log("this.selectedRegion",this.selectedRegion)

        this.lastUsedColor = null;

        this.reclusterizeClusteredFlatTree();

        const processCluster = (cb: (cluster: ClusterizedFlatTreeNode, x: number, y: number, w: number) => void) => {
            return (cluster: ClusterizedFlatTreeNode) => {
                const { start, duration, level } = cluster;
                const { x, y, w } = this.calcRect(start, duration, level);

                if (x + w > 0 && x < width && y + blockHeight > 0 && y < height) {
                    cb(cluster, x, y, w);
                }
            };
        };

        const renderCluster = (cluster: ClusterizedFlatTreeNode, x: number, y: number, w: number) => {
            const { type, nodes, color, pattern, badge } = cluster;
            const mouse = this.interactionsEngine.getMouse();

            if (mouse.y >= y && mouse.y <= y + blockHeight) {
                addHitRegion(cluster, x, y, w);
            }

            if (w >= 0.25) {
                this.renderEngine.addRect({ color: this.getColor(type, color), pattern, x, y, w }, 0);

                if (badge) {
                    const badgePatternName = `node-badge-${badge}`;
                    const badgeWidth = (this.renderEngine.styles.badgeSize * 2) / Math.SQRT2;

                    this.renderEngine.createCachedDefaultPattern({
                        name: badgePatternName,
                        type: 'triangles',
                        config: {
                            color: badge,
                            width: badgeWidth,
                            align: 'top',
                            direction: 'top-left',
                        },
                    });

                    this.renderEngine.addRect(
                        {
                            pattern: badgePatternName,
                            color: 'transparent',
                            x,
                            y,
                            w: Math.min(badgeWidth, w),
                        },
                        1,
                    );
                }
            }

            if (w >= minTextWidth && nodes.length === 1) {
                this.renderEngine.addText({ text: nodes[0].source.name, x, y, w }, 2);
            }
        };

        const addHitRegion = (cluster: ClusterizedFlatTreeNode, x: number, y: number, w: number) => {
            this.interactionsEngine.addHitRegion(RegionTypes.CLUSTER, cluster, x, y, w, blockHeight);
        };
        this.actualClusterizedFlatTree.forEach(processCluster(renderCluster));

        if (selectedNodes.size>0) {
            if(this.isMultiple){
                this.addBackgroundColorToNodes(selectedNodes,this.selectedColor)
            }else{
                const {
                    source: { start=0, duration=0 },
                    level,
                } = this?.selectedRegion?.data;
                if(this?.selectedRegion?.data){
                    const { x, y, w } = this.calcRect(start, duration, level);
                    this.renderEngine.addRect({ color: this.selectedColor as string, x, y, w, h: this.renderEngine.blockHeight });
                }
                
            }
        }

        clearTimeout(this.renderChartTimeout);

        this.renderChartTimeout = window.setTimeout(() => {
            this.interactionsEngine.clearHitRegions();
            this.actualClusterizedFlatTree.forEach(processCluster(addHitRegion));
        }, 16);
    }
}
